package com.cloud.gateway.component;

import com.alibaba.fastjson.JSONObject;
import com.cloud.gateway.util.Base64Util;
import com.cloud.gateway.util.Md5Util;
import com.cloud.gateway.util.ShaUtil;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Class AuthClient
 * ak,sk鉴权客户端
 */
public class AuthClient {

    public String accessKey;
    //密钥约定sk
    public String secretAccessKey;
    //请求payload加密串
    public String contentMD5;
    //请求header头类型
    public String contentType;
    //请求生成的时间
    public String date;
    //客户端生成32位随机字符串,请求体中5分钟内不能重复，重复则回复nonce重复，重新请求
    public String nonce;

    /**
     * StringToSign生成规则如下(没有的部分不用拼，如响应头中，没有uri，则StringToSign中不用拼URI项)：
     * StringToSign =  Method + "\n"
     * + URI + "\n"
     * + Content-MD5 + "\n"
     * + Content-Type + "\n"
     * + CanonicalizedHeaders)
     */
    public String signature;

    public AuthClient()
    {
    }

    public AuthClient(String accessKey,String secretAccessKey)
    {
        this.accessKey = accessKey;
        this.secretAccessKey = secretAccessKey;
    }


    public String getContentMD5(String param)
    {
        if(null == param)
        {
            param = JSONObject.toJSONString(new JSONObject());
        }
        return Base64Util.encodeToByte(Md5Util.toBytes(param));
    }

    /**
     * map 按 key 升序排序
     *
     * @param map
     * @return
     */
    private Map<String, String> sortByKey(Map<String, String> map) {
        Map<String, String> result = new LinkedHashMap<>(map.size());
        map.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    public Map<String,String> getCanonicalHeaders()
    {
        Map<String,String> data = new HashMap<String,String>();
        data.put("x-date",this.date);
        data.put("x-username",this.accessKey);
        data.put("x-nonce",this.nonce);

        return sortByKey(data);
    }

    private void genInfo(String reqData,String method,String url,String contentType)
    {
        this.contentMD5 = getContentMD5(reqData);
        this.date = getDate();
        this.nonce = getNonce();
        this.contentType = contentType;
        this.signature = genSign(method,url);
    }


    public String genSign(String method,String url)
    {
        String strToSign = getStrSign(method,url,this.contentMD5,this.contentType);
        String lowerSecretAccessKey = getLowerSecretAccessKey(this.secretAccessKey);

        return Base64Util.encodeToByte(ShaUtil.hmacSha256ToBytes(strToSign,lowerSecretAccessKey));
    }

    private String getLowerSecretAccessKey(String secretAccessKey)
    {
        return Md5Util.stringToMD5(secretAccessKey).toLowerCase();
    }

    /**
     *
     * StringToSign生成规则如下(没有的部分不用拼，如响应头中，没有uri，则StringToSign中不用拼URI项)：
     * StringToSign =  Method + "\n"
     * + URI + "\n"
     * + Content-MD5 + "\n"
     * + Content-Type + "\n"
     * + getCanonicalHeaders
     * @param method
     * @param url
     * @param contentMD5
     * @param contentType
     * @return
     */
    private String getStrSign(String method,String url,String contentMD5,String contentType)
    {
        String methodUpper = method.toUpperCase();
        Map<String,String> canonicalHeaders = getCanonicalHeaders();

        StringBuilder str = new StringBuilder();
        str.append(methodUpper).append("\n")
                .append(url).append("\n")
                .append(contentMD5).append("\n")
                .append(contentType).append("\n");

        for(Map.Entry<String, String> entry : canonicalHeaders.entrySet()){
            str.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
        }

        return str.toString();

    }


    public AuthReqHeader getHeader(String reqData,String method,String url,String contentType)
    {
        genInfo(reqData,method,url,contentType);

        AuthReqHeader authReqHeader = new AuthReqHeader();
        authReqHeader.setContentMd5(this.contentMD5);
        authReqHeader.setxDate(this.date);
        authReqHeader.setxUsername(this.accessKey);
        authReqHeader.setxNonce(this.nonce);
        authReqHeader.setxSignature(this.signature);

        return authReqHeader;
    }

    private String getDate()
    {
        Date date = new Date();
        SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        return dateFormat.format(date);
    }

    private String getNonce()
    {
        return Md5Util.stringToMD5(UUID.randomUUID().toString().replaceAll("-", ""));
    }
}
